import threading
import time

from Foundation import *

import evermore



class ConnectionHandler(threading.Thread):
    """
    Handle the connection to a GPS device over a serial interface. This implies
    checking periodically to see if the device is connected/disconnected,
    sending commands to it and reading data from it.

    Communication uses the EverMore Python module.
    """
    def __init__(self, name='ConnectionHandler', controller=None):
        """
        Connect to the GPS.
        """
        threading.Thread.__init__(self, name=name)
        self._controller = controller
        self._connected = False
        self.stop = False
        self.idle = False
        self._gps = None
        
        try:
            self._gps = evermore.EverMoreGPS()
        except:
            # Tell the controller that the drivers for the GPS have not been
            # installed.
            self._controller.fatalError('USB drivers not installed.')
        return

    def run(self):
        """
        Main event loop: just sit tight waiting for requests.
        """
        pool = NSAutoreleasePool.alloc().init()
        
        try:
            while(not self.stop):
                time.sleep(0.1)
                
                if(not self._connected):
                    # Try to connect.
                    connected = self._gps.testConnection()
                    if(connected):
                        self._controller.gpsDidConnect()
                        self._connected = True
                        self.idle = True
                    else:
                        continue
                    # <-- end if
                else:
                    # We are connected, now check and see if the GPS was
                    # disconnected.
                    self._connected = self._gps.testConnection()
                    if(not self._connected):
                        self._controller.gpsDidDisconnect()
                        self.idle = False
                        continue
                    # <-- end if
                # <-- end if
                
                # We are connected, let's read the data.
                if(self.idle):
                    self.readRealTimePosition()
                # <-- end if
            # <-- end while
        finally:
            del(pool)
            if(self._gps):
                self._gps.close()
            # <-- end if
        # <-- end try
        return

    def readRealTimePosition(self):
        # Read the data from the GPS.
        print('ciao')
        data = self._gps.readline()
        # Since our way to tell if the GPS is connected is a HACK (we read the
        # first characted from the data stream), we cannot test for $GPGGA, only
        # for GPGGA. Need a better way to do this.
        if(data.startswith('GPGGA')):
            self._controller.displayStreamData(data)
        # <-- end if
        return

    def join(self, timeout=None):
        """
        Overrides the default join() method by closing the connection with the
        GPS.
        """
        if(self._gps):
            self._gps.close()
        #<-- end if
        
        self.stop = True
        threading.Thread.join(self, timeout)
        return

    def __getattr__(self, name):
        """
        This is the way we pass method calls to self._gps.
        """
        if(hasattr(self._gps, name)):
            return(getattr(self._gps, name))
        return(self._controller.error('The GPS does not respond to %s' %(name)))

    def gpsStatus(self):
        """
        Check the connection to the device. If connected, retrieve basic info.
        """
        self.idle = False
        firmware = None
        percentFull = None
        numPoints = None
        
        # Ask for revision info and then log data status.
        (date, relVersion, relType) = self._gps.getRevisionInformation()
        date = str(date)
        firmware = '%s %s (%s-%s-%s)' % (relVersion,
                                         relType,
                                         date[:2],
                                         date[2:4],
                                         date[4:])
        (numPoints, percentFull) = self.memoryStatus()
        self.idle = True
        return(firmware, percentFull, numPoints)

    def memoryStatus(self):
        """
        Get the status of the on-board memory.
        """
        return(self._gps.getLogStatus()[:2])

    def getLogData(self):
        """
        Just a thin wrapper around the method with the same name in EverMoreGPS.
        We simply handle the idle state...
        """
        self.idle = False
        data = self._gps.getLogData()
        self.idle = True
        return(data)
